#if !defined(MAP_UTILITY_IMPL_H_)
#define MAP_UTILITY_IMPL_H_
#include <boost/json.hpp>
#include <cstring>
#include <filesystem>
#include <iostream>
#include <list>

#include "common/map_info.h"

namespace boost::json {
/// @brief roadedge property name in json
static std::string const& sRoadEdgeProperty{"road_edges"};
/// @brief lane divider perperty name in json
static std::string const& sLaneDividerProperty{"lane_dividers"};
/// @brief center line perperty name in json
static std::string const& sCenterLineProperty{"center_lines"};
/// @brief lane divider joint perperty name in json
static std::string const& sLaneDividerJointProperty{"lane_divider_joints"};
/// @brief lane divider id1 perperty name in json
static std::string const& sID1Property{"lane_divider_id1"};
/// @brief lane divider id2 perperty name in json
static std::string const& sID2Property{"lane_divider_id2"};
/// @brief lane divider point perperty name in json
static std::string const& sPointProperty{"point"};

/// @brief road marking perperty name in json
static std::string const& sRoadMarkingProperty{"road_markings"};
/// @brief center perperty name in json
static std::string const& sCenterProperty{"center"};
/// @brief length perperty name in json
static std::string const& sLengthProperty{"length"};
/// @brief width perperty name in json
static std::string const& sWidthProperty{"width"};
/// @brief height perperty name in json
static std::string const& sHeightProperty{"height"};
/// @brief yaw perperty name in json
static std::string const& sYawProperty{"yaw"};

/// @brief id property name in json
static std::string const& sIDProperty{"id"};
/// @brief category property name in json
static std::string const& sCategoryProperty{"category"};
/// @brief points property name in json
static std::string const& sPointsProperty{"points"};
/// @brief polygon property name in json
static std::string const& sPolygonProperty{"polygon"};
/// @brief LineType property name in json
static std::string const& sLineTypeProperty{"line_type"};
/// @brief Color property name in json
static std::string const& sColorProperty{"color"};
/// @brief topo ids property name in json
static std::string const& sCenterLineTopoIDsProperty{"topo_point_ids"};

using JPoint = std::vector<double>;

using JPointSet = std::list<JPoint>;

autodrive::perception::MapElementPoint tag_invoke(
    boost::json::value_to_tag<autodrive::perception::MapElementPoint>,
    boost::json::value const& jv);

autodrive::perception::MapElementPointSet tag_invoke(
    boost::json::value_to_tag<autodrive::perception::MapElementPointSet>,
    boost::json::value const& jv);

void tag_invoke(boost::json::value_from_tag, boost::json::value& jv,
                autodrive::perception::MapElementPoint const& point);

void tag_invoke(boost::json::value_from_tag, boost::json::value& jv,
                autodrive::perception::MapElementPointSet const& pointSet);

void tag_invoke(boost::json::value_from_tag, boost::json::value& jv,
                autodrive::perception::RoadEdge const& roadEdge);

void tag_invoke(boost::json::value_from_tag, boost::json::value& jv,
                autodrive::perception::LaneDivider const& lanedivider);

void tag_invoke(boost::json::value_from_tag, boost::json::value& jv,
                autodrive::perception::CenterLine const& centerLine);

void tag_invoke(
    boost::json::value_from_tag, boost::json::value& jv,
    autodrive::perception::LaneDividerJoint const& lanedividerJoint);

void tag_invoke(boost::json::value_from_tag, boost::json::value& jv,
                autodrive::perception::RoadMarking const& roadMarking);

void tag_invoke(boost::json::value_from_tag, boost::json::value& jv,
                autodrive::perception::Map const& map);

}  // namespace boost::json

namespace autodrive {
namespace perception {

void parse_road_edge(boost::json::value const& jv, RoadEdge& roadEdge) noexcept;

void parse_lane_divider(boost::json::value const& jv,
                        LaneDivider& laneDivider) noexcept;

void parse_center_line(boost::json::value const& jv,
                       CenterLine& centerLine) noexcept;

void parse_lane_divider_joint(boost::json::value const& jv,
                              LaneDividerJoint& laneDividerJoint) noexcept;

void parse_road_marking(boost::json::value const& jv,
                        RoadMarking& roadMarking) noexcept;

/// @brief clip data in range
/// @tparam T output data type
/// @param data data to clip
/// @return clipped data
template <typename T>
T clip(double data) {
  static_assert(std::is_arithmetic_v<T>, "clip type is not arithmetic type");
  bool isNegative{false};
  if (std::is_floating_point_v<T> && data < 0) {
    data = -data;
    isNegative = true;
  }

  auto value{std::clamp<double>(data, std::numeric_limits<T>::min(),
                                std::numeric_limits<T>::max())};
  if (isNegative) {
    value = -value;
  }

  return static_cast<T>(value);
}
}  // namespace perception
}  // namespace autodrive

#endif  // MAP_UTILITY_IMPL_H_
